package cn.turboinfo.fuyang.api.domain.mini.usecase.custom;

import cn.turboinfo.fuyang.api.domain.common.service.custom.ServiceCustomService;
import cn.turboinfo.fuyang.api.domain.common.service.order.PayOrderService;
import cn.turboinfo.fuyang.api.domain.common.service.user.UserThirdPartyService;
import cn.turboinfo.fuyang.api.domain.common.service.wechat.WechatPayService;
import cn.turboinfo.fuyang.api.domain.common.usecase.AbstractUseCase;
import cn.turboinfo.fuyang.api.domain.util.CurrencyUnitUtils;
import cn.turboinfo.fuyang.api.entity.common.constant.WechatAttachConstants;
import cn.turboinfo.fuyang.api.entity.common.enumeration.common.EntityObjectType;
import cn.turboinfo.fuyang.api.entity.common.enumeration.order.PayOrderStatus;
import cn.turboinfo.fuyang.api.entity.common.enumeration.order.PayType;
import cn.turboinfo.fuyang.api.entity.common.enumeration.order.ServiceOrderPayStatus;
import cn.turboinfo.fuyang.api.entity.common.enumeration.user.ThirdPartyType;
import cn.turboinfo.fuyang.api.entity.common.pojo.custom.ServiceCustom;
import cn.turboinfo.fuyang.api.entity.common.pojo.order.PayOrder;
import cn.turboinfo.fuyang.api.entity.common.pojo.order.PayOrderCreator;
import cn.turboinfo.fuyang.api.entity.common.pojo.user.UserThirdParty;
import com.github.binarywang.wxpay.config.WxPayConfig;
import com.github.binarywang.wxpay.v3.util.SignUtils;
import lombok.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import javax.validation.constraints.NotNull;
import javax.validation.constraints.Positive;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.List;
import java.util.Objects;

/**
 * 消费者提交下单时的支付请求
 *
 * @author sunshow
 */
@Slf4j
@RequiredArgsConstructor
@Component
public class MiniConsumerSubmitCustomDepositPayUseCase extends AbstractUseCase<MiniConsumerSubmitCustomDepositPayUseCase.InputData, MiniConsumerSubmitCustomDepositPayUseCase.OutputData> {

    private final ServiceCustomService serviceCustomService;

    private final PayOrderService payOrderService;

    private final WechatPayService wechatPayService;

    private final UserThirdPartyService userThirdPartyService;

    @Override
    protected OutputData doAction(InputData inputData) {
        Long userId = inputData.getUserId();
        Long serviceCustomId = inputData.getServiceCustomId();
        PayType payType = inputData.getPayType();

        ServiceCustom serviceCustom = serviceCustomService.getByIdEnsure(serviceCustomId);

        if (!Objects.equals(userId, serviceCustom.getUserId())) {
            throw new IllegalArgumentException("用户不匹配");
        }

        if (serviceCustom.getPayStatus() != ServiceOrderPayStatus.UNPAID) {
            throw new IllegalArgumentException("订单已支付");
        }

        Long payOrderId;

        // TODO 根据不同支付类型做不同的行为
        switch (payType) {
            case POST_PAID -> {
                // 生成新的待支付订单
                PayOrder payOrder = payOrderService.save(
                        PayOrderCreator.builder()
                                .withUserId(userId)
                                .withPayOrderStatus(PayOrderStatus.UNPAID)
                                .withPayType(PayType.POST_PAID)
                                .withObjectType(EntityObjectType.SERVICE_CUSTOM)
                                .withObjectId(serviceCustomId)
                                .withAmount(BigDecimal.ZERO)
                                .withTransactionId(StringUtils.EMPTY)
                                .build()
                );
                payOrderId = payOrder.getId();
                // 直接设置服务订单支付状态
                serviceCustomService.submitCustomPostPaid(serviceCustomId, payOrderId);
            }
            case WECHAT -> {
                // 查询是否存在未支付订单
                List<PayOrder> payOrderList = payOrderService.findByObjectIdAndStatusAndPayType(EntityObjectType.SERVICE_CUSTOM, serviceCustomId, PayOrderStatus.UNPAID, PayType.WECHAT);

                if (payOrderList.size() > 0) {
                    // 如果存在则取消原有支付订单
                    payOrderList.forEach(it -> payOrderService.checkAndUpdateStatus(it.getId(), PayOrderStatus.CANCELLED, PayOrderStatus.UNPAID));
                }
                // 生成新的待支付订单
                PayOrder payOrder = payOrderService.save(
                        PayOrderCreator.builder()
                                .withUserId(userId)
                                .withPayOrderStatus(PayOrderStatus.UNPAID)
                                .withPayType(PayType.WECHAT)
                                .withObjectType(EntityObjectType.SERVICE_CUSTOM)
                                .withObjectId(serviceCustomId)
                                .withAmount(serviceCustom.getDeposit())
                                .build()
                );
                payOrderId = payOrder.getId();
                // 查询用户 open id
                UserThirdParty userThirdParty = userThirdPartyService.getByUserId(userId, ThirdPartyType.MINI_WECHAT).orElseThrow(() -> new RuntimeException("用户未绑定微信"));

                // 生成微信支付订单
                String prepaymentId = wechatPayService.generatePrepayment(
                        "定制服务-定金支付",
                        payOrderId,
                        CurrencyUnitUtils.getCentsAmount(serviceCustom.getDeposit()),
                        userThirdParty.getThirdPartyAccount(),
                        WechatAttachConstants.WECHAT_ATTACH_PREPAY
                );

                LocalDateTime now = LocalDateTime.now();
                String timeStamp = String.valueOf(now.toEpochSecond(ZoneOffset.of("+8")));
                String signType = "RSA";
                String nonceStr = SignUtils.genRandomStr();
                String packageStr = String.format("prepay_id=%s", prepaymentId);
                WxPayConfig config = wechatPayService.getConfig();
                return MiniConsumerSubmitCustomDepositPayUseCase.OutputData.builder()
                        .payOrderId(payOrderId)
                        .nonceStr(nonceStr)
                        .timeStamp(timeStamp)
                        .signType(signType)
                        .packageStr(packageStr)
                        .paySign(SignUtils.sign(String.format("%s\n%s\n%s\n%s\n", config.getAppId(), timeStamp, nonceStr, packageStr), config.getPrivateKey()))
                        .build();

            }
            default -> throw new RuntimeException("不支持的支付类型");
        }

        return OutputData.builder()
                .payOrderId(payOrderId)
                .build();
    }

    @EqualsAndHashCode(callSuper = true)
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    @Builder
    public static class InputData extends AbstractUseCase.InputData {

        @NotNull(message = "用户不能为空")
        @Positive
        private Long userId;

        @NotNull(message = "订单不能为空")
        @Positive
        private Long serviceCustomId;

        @NotNull(message = "支付类型不能为空")
        private PayType payType;

    }

    @Getter
    @Setter
    @Builder
    public static class OutputData extends AbstractUseCase.OutputData {

        /**
         * 支付订单号
         */
        private Long payOrderId;

        /**
         * 微信支付相关参数 时间戳
         */
        private String timeStamp;

        /**
         * 微信支付相关参数 随机字符串
         */
        private String nonceStr;

        /**
         * 微信支付相关参数 订单详情扩展字符串
         */
        private String packageStr;

        /**
         * 微信支付相关参数 签名方式
         */
        private String signType;

        /**
         * 微信支付相关参数 签名
         */
        private String paySign;

    }
}
